package com.yuntsg.ruiijn.JiaXinZhi;

import cn.hutool.core.collection.ConcurrentHashSet;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;

import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 本程序的功能是对指定的单位的查重管理后台，展示本单位的科室、基金、撤稿、学术不端、第一作者、通讯作者的分布情况。
 * 贾新志 2023.07.13
 */

public class JiaXinZhiIndexController {


    // type为firstAuth是第一作者，为tongXun是通讯作者，两个共用一个程序，年份为最近年。
    public static List<NameIdListSum> getFirstAuthSum(List<ArticleInfo> articleInfoList, List<Integer> yearList, String type) {
        // 第一次循环，因为程序不知道有哪些年，而用户又没有指定，所以，需要先获取每年的篇数
        if (yearList.size() < 1) {
            Map<Integer, Integer> yearAndSumMap = new HashMap<>();
            for (ArticleInfo articleInfo : articleInfoList) {
                int year = articleInfo.getYear();
                String deptName = articleInfo.getDeptName();
                if (deptName.length() > 1) {
                    yearAndSumMap.compute(year, (key, value) -> value != null ? (value + 1) : 1);
                }
            }
            yearList.add(getMaxYear(yearAndSumMap));
        }
        Map<String, Set<Integer>> deptAndIdSetMap = new HashMap<>();
        // 第二次循环，本次已经有了指定的年，此时，科室不能为空，年应在指定的年的范围内，且为第一作者或通讯作者，三个条件均满足，则给添加上去。
        for (ArticleInfo articleInfo : articleInfoList) {
            int year = articleInfo.getYear();
            String deptName = articleInfo.getDeptName();
            int value = type.equals("firstAuth") ? articleInfo.getIsFirstAuth() : articleInfo.getIsTongXun();
            int id = articleInfo.getId();
            if (deptName.length() > 1 && value == 1 && yearList.contains(year)) {
                putStrSetInt(deptName, id, deptAndIdSetMap);
            }
        }
        return addNameIdListSum(deptAndIdSetMap, 0);
    }

    // 学术不端和撤稿，默认是全部年，只有勾选了，才是指定的年份，当勾选了则yearList.size() > 0，需要进行判定是否包含。type为misconduct是学术不端，为retraction是撤稿，两个共用一个程序。
    public static List<NameIdListSum> getMisconductSum(List<ArticleInfo> articleInfoList, List<Integer> yearList, String type) {
        Map<String, Set<Integer>> deptAndIdSetMap = new HashMap<>();
        for (ArticleInfo articleInfo : articleInfoList) {
            int year = articleInfo.getYear();
            String deptName = articleInfo.getDeptName();
            int value = type.equals("retraction") ? articleInfo.getIsRetraction() : articleInfo.getMisconduct();
            int id = articleInfo.getId();
            if (deptName.length() > 1 && value == 1) {
                if (yearList.size() > 0) {
                    if (yearList.contains(year)) {
                        putStrSetInt(deptName, id, deptAndIdSetMap);
                    }
                } else {
                    putStrSetInt(deptName, id, deptAndIdSetMap);
                }
            }
        }
        return addNameIdListSum(deptAndIdSetMap, 0);
    }

    // 如果输入年份的集合为空，则取最近的年份，本方法不考虑是否有科室。
    public static List<NameIdListSum> getFundArticleSum(List<ArticleInfo> articleInfoList, List<Integer> yearList, int limit) {
        // 第一次循环，因为程序不知道有哪些年，而用户又没有指定，所以，需要先获取每年的篇数
        if (yearList.size() < 1) {
            Map<Integer, Integer> yearAndSumMap = new HashMap<>();
            for (ArticleInfo articleInfo : articleInfoList) {
                int year = articleInfo.getYear();
                String deptName = articleInfo.getDeptName();
                if (deptName.length() > 1) {
                    yearAndSumMap.compute(year, (key, value) -> value != null ? (value + 1) : 1);
                }
            }
            yearList.add(getMaxYear(yearAndSumMap));
        }
        Map<String, Set<Integer>> fundAndIdSetMap = new HashMap<>();
        // 第二次循环，本次已经有了指定的年，此时，科室不能为空，年应在指定的年的范围内，两个条件均满足，则给添加上去。
        for (ArticleInfo articleInfo : articleInfoList) {
            List<String> funds = articleInfo.getFundList();
            int year = articleInfo.getYear();
            int id = articleInfo.getId();
            for (String fund : funds) {
                if (fund.length() > 2 && yearList.contains(year)) {
                    putStrSetInt(fund, id, fundAndIdSetMap);
                }
            }
        }
        return addNameIdListSum(fundAndIdSetMap, limit);
    }

    public static List<NameIdListSum> addNameIdListSum(Map<String, Set<Integer>> fundAndIdSetMap, int limit) {
        // 将基金名称相对应的id集合，转变成对应的论文篇数，再对此进行排序，即论文数量最多的基金名称，显示在第一个。
        Map<String, Integer> fundAndSumMap = new HashMap<>();
        for (Map.Entry<String, Set<Integer>> entry : fundAndIdSetMap.entrySet()) {
            fundAndSumMap.put(entry.getKey(), entry.getValue().size());
        }
        List<NameIdListSum> fundArticleSumList = new ArrayList<>();
        Map<String, Integer> mapSortValueStrInt = limit > 0 ? mapSortValueIntLen(fundAndSumMap, limit) : mapSortValueStrInt(fundAndSumMap);

        // 根据排序后的基金，再获取它的id的集合，篇数，存入返回的结果集合中。
        for (Map.Entry<String, Integer> entry : mapSortValueStrInt.entrySet()) {
            String fund = entry.getKey();
            Integer sum = entry.getValue();
            if (fundAndIdSetMap.containsKey(fund)) {
                NameIdListSum nameIdListSum = new NameIdListSum();
                nameIdListSum.setName(fund);
                nameIdListSum.setIdList(new ArrayList<>(fundAndIdSetMap.get(fund)));
                nameIdListSum.setSum(sum);
                fundArticleSumList.add(nameIdListSum);
            }
        }
        return fundArticleSumList;
    }

    // 对Map进行排序，此排序为最大的在前面，输入一个map，再返回一个排序后的新map，String, Integer类型，本排序增加了过滤功能，可以设置要前多少（len）个。
    public static Map<String, Integer> mapSortValueIntLen(Map<String, Integer> mapName, Integer len) {
        Map<String, Integer> result = new LinkedHashMap<>();
        mapName.entrySet().stream()
                .sorted(Map.Entry.<String, Integer>comparingByValue()
                        .reversed()).limit(len).forEachOrdered(e -> result.put(e.getKey(), e.getValue()));
        return result;
    }

    // 如果输入年份的集合为空，则取最近的年份，如果输入的基金名称集合为空，则取最多的基金名称
    public static List<NameIdListSum> getFundYearName(List<ArticleInfo> articleInfoList, List<Integer> yearList, List<String> fundList) {
        // 第一次循环，因为程序不知道有哪些年，有哪些基金的名称，而用户又没有指定，所以，需要先获取每年的篇数，以及各个基金的篇数
        if (yearList.size() < 1 || fundList.size() < 1) {
            Map<String, Integer> fundAndSumMap = new HashMap<>();
            Map<Integer, Integer> yearAndSumMap = new HashMap<>();
            for (ArticleInfo articleInfo : articleInfoList) {
                List<String> funds = articleInfo.getFundList();
                int year = articleInfo.getYear();
                String deptName = articleInfo.getDeptName();
                if (deptName.length() > 1) {
                    for (String fund : funds) {
                        fundAndSumMap.compute(fund, (key, value) -> value != null ? (value + 1) : 1);
                    }
                    yearAndSumMap.compute(year, (key, value) -> value != null ? (value + 1) : 1);
                }
            }
            // 如果用户没有指定年，则取最近的年
            if (yearList.size() < 1) {
                yearList.add(getMaxYear(yearAndSumMap));
            }
            // 如果用户没有指定基金的名称，则取最多的基金名称
            if (fundList.size() < 1) {
                fundList.add(getMaxFund(fundAndSumMap));
            }
        }

        Map<String, Set<Integer>> deptAndIdSetMap = new HashMap<>();
        // 第二次循环，本次已经有了指定的年和指定的基金名称，此时，科室不能为空，年应在指定的年的范围内，基金也应该在指定的基金集合中，三个条件均满足，则给添加上去。
        for (ArticleInfo articleInfo : articleInfoList) {
            List<String> funds = articleInfo.getFundList();
            int year = articleInfo.getYear();
            String deptName = articleInfo.getDeptName();
            int id = articleInfo.getId();
            if (deptName.length() > 1 && yearList.contains(year) && isRetainAll(funds, fundList)) {
                putStrSetInt(deptName, id, deptAndIdSetMap);
            }
        }
        return addNameIdListSum(deptAndIdSetMap, 0);
    }

    // 对Map进行排序，此排序为最大的在前面，输入一个map，再返回一个排序后的新map，String, Integer类型，本排序增加了过滤功能，可以设置要前多少（len）个。
    public static Map<String, Integer> mapSortValueStrInt(Map<String, Integer> mapName) {
        Map<String, Integer> result = new LinkedHashMap<>();
        mapName.entrySet().stream()
                .sorted(Map.Entry.<String, Integer>comparingByValue()
                        .reversed()).forEachOrdered(e -> result.put(e.getKey(), e.getValue()));
        return result;
    }

    public static void putStrSetInt(String dept, Integer id, Map<String, Set<Integer>> deptAndIdSetMap) {
        deptAndIdSetMap.compute(dept, (k, v) -> {
            if (v == null) {
                v = new ConcurrentHashSet<>();
            }
            v.add(id);
            return v;
        });
    }

    // 如果两个集合有交集，返回true，否则返回false。
    public static boolean isRetainAll(List<String> funds, List<String> fundList) {
        for (String fund : fundList) {
            if (funds.contains(fund)) {
                return true;
            }
        }
        return false;
    }

    // 获取发文篇数最多的基金的名称
    public static String getMaxFund(Map<String, Integer> fundAndSumMap) {
        int sum = 0;
        String fund = "";
        for (Map.Entry<String, Integer> entry : fundAndSumMap.entrySet()) {
            if (entry.getValue() > sum) {
                fund = entry.getKey();
                sum = entry.getValue();
            }
        }
        return fund;
    }

    // 获取最合适的年份，默认要最近的一年，如果没有，则取上一年的。
    public static int getMaxYear(Map<Integer, Integer> yearAndSumMap) {
        Set<Integer> yearSet = yearAndSumMap.keySet();
        int nowYear = getNowYear();
        if (yearSet.contains(nowYear)) {
            return nowYear;
        } else {
            for (int i = nowYear; i > 2017; i--) {
                if (yearSet.contains(i)) {
                    return i;
                }
            }
        }
        return 2018;
    }

    // 获取现在年，方法是当前时间戳减去8个月后再取年，即2021年7月1日是2021年，2021年6月1日是2020年。此用于新词发现，最长的比较就是一年半与之前的所有相比。
    public static int getNowYear() {
        long time = System.currentTimeMillis() - 60 * 60 * 24 * 30 * 6 * 1000L;
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy");
        String format = sdf.format(new Date(time));
        if (NumberUtil.isInteger(format)) {
            return Integer.parseInt(format);
        }
        return 1900;
    }

    public static Map<Integer, List<ArticleInfo>> readArticleTable(String tableName, String autoId, String field, Map<Integer, Map<Integer, List<String>>> idAndFundListMap) throws SQLException {
        Map<Integer, List<ArticleInfo>> map = new HashMap<>();
        List<Entity> unit_list = Db.use().query("select * from unit_basic ");
        unit_list.parallelStream().forEach(e -> {
            Integer gid = e.getInt("id");
            List<ArticleInfo> articleInfoList = new ArrayList<>();
            int onePageNum = 1000;
            List<Entity> tableNum = null;
            try {
                tableNum = Db.use().query("select count(1) as count from " + tableName);
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            if (tableNum.get(0).getInt("count") > 0) {
                int tableMax = 0;
                try {
                    tableMax = Db.use().queryNumber("select max(" + autoId + ") from " + tableName).intValue();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
                Collection<Integer> tableList = new ArrayList<>();
                NumberUtil.appendRange(0, tableMax + onePageNum, onePageNum, tableList);
                for (Integer startId : tableList) {
                    try {
                        List<Entity> tableData = Db.use().query("select `" + autoId + "`,`" + field + "` from " + tableName + " where gid = " + gid + " and  so_type = 1 and  " + autoId + "   between " + startId + " and " + (startId + onePageNum - 1));
                        for (Entity entry : tableData) {
                            int id = getIntField(entry, "id"); // 是否为第一作者，是为1，否为0；
                            String deptName = getStrField(entry, "dept_name"); // 科室的名称，一篇论文只能归到一个科室下面
                            String c_py = getStrField(entry, "c_py"); // 论文发表的年份
                            int year = NumberUtil.isInteger(c_py) ? Integer.parseInt(c_py) : 2018;
                            int isFirstAuth = getIntField(entry, "co_flag"); // 是否为第一作者，是为1，否为0；
                            //TODO 不管是片内还是全网 都认为是学术不端 ZFL231110
                            if (isFirstAuth == 2) {
                                isFirstAuth = 1;
                            }
                            int isTongXun = getIntField(entry, "rp_flag"); // 是否为通讯作者，是为1，否为0；
                            int misconduct = getIntField(entry, "copy_flag"); // 是否为学术不端，0待定 1是 2否 3疑似 4其它
                            int isRetraction = getIntField(entry, "withdraw_flag"); // 是否是撤稿，是为1，否为0；
                            List<String> fundList = idAndFundListMap.get(gid).containsKey(id) ? idAndFundListMap.get(gid).get(id) : new ArrayList<>();
                            ArticleInfo articleInfo = new ArticleInfo();
                            articleInfo.setId(id);
                            articleInfo.setDeptName(deptName);
                            articleInfo.setYear(year);
                            articleInfo.setFundList(fundList);
                            articleInfo.setMisconduct(misconduct);
                            articleInfo.setIsRetraction(isRetraction);
                            articleInfo.setIsFirstAuth(isFirstAuth);
                            articleInfo.setIsTongXun(isTongXun);
                            articleInfoList.add(articleInfo);
                        }
                    } catch (SQLException e1) {
                        e1.printStackTrace();
                    }
                }
            }
            map.put(gid, articleInfoList);
        });


        return map;
    }

    /**
     * @param tableName check_fund
     * @param autoId
     * @param field
     * @return
     * @throws SQLException
     */
    public static Map<Integer, Map<Integer, List<String>>> readFundTable(String tableName, String autoId, String field) throws SQLException {
        Map<Integer, Map<Integer, List<String>>> idAndFundListMapFinal = new HashMap<>();
        List<Entity> unit_list = Db.use().query("select * from unit_basic ");

        for (Entity entity : unit_list) {
            Integer gid = entity.getInt("id");
            Map<Integer, List<String>> idAndFundListMap = new HashMap<>();
            int onePageNum = 1000;
            List<Entity> tableNum = Db.use().query("select count(1) as count from " + tableName);
            if (tableNum.get(0).getInt("count") > 0) {
                int tableMax = Db.use().queryNumber("select max(" + autoId + ") from " + tableName).intValue();
                Collection<Integer> tableList = new ArrayList<>();

                NumberUtil.appendRange(0, tableMax + onePageNum, onePageNum, tableList);

                for (Integer startId : tableList) {
                    try {
                        List<Entity> tableData = Db.use().query("select `" + autoId + "`,`" + field + "` from " + tableName + " where gid =  " + gid + " and " + autoId + " between " + startId + " and " + (startId + onePageNum - 1));
                        for (Entity entry : tableData) {
                            String ids = getStrField(entry, "ids");
                            if (ids.length() > 2) {
                                List<Integer> idList = JSONObject.parseArray(ids, Integer.class);
                                String oldName = getStrField(entry, "old_name");
                                String newName = getStrField(entry, "new_name");
                                newName = newName.length() > 2 ? newName : oldName;
                                if (newName.length() > 2) {
                                    for (Integer id : idList) {
                                        putIntListStr(idAndFundListMap, id, newName);
                                    }
                                }
                                /**
                                 * ZFL
                                 */
                                String cn_name = getStrField(entry, "cn_name");
                                if (cn_name.length() > 2) {
                                    for (Integer id : idList) {
                                        putIntListStr(idAndFundListMap, id, cn_name);
                                    }
                                }
                            }
                        }
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
            idAndFundListMapFinal.put(gid, idAndFundListMap);
        }


        return idAndFundListMapFinal;
    }

    public static void putIntListStr(Map<Integer, List<String>> idAndFundListMap, Integer id, String fund) {
        idAndFundListMap.compute(id, (k, v) -> {
            if (v == null) {
                v = Collections.synchronizedList(new ArrayList<String>());
            }
            v.add(fund);
            return v;
        });
    }

    // 返回Integer类型的字段的值，field为字段的名称。
    public static int getIntField(Entity entry, String field) {
        if (null != entry) {
            Integer num = entry.getInt(field);
            if (null != num && num > 0) {
                return num;
            }
        }
        return 0;
    }

    // 返回String类型的字段的值，field为字段的名称。
    public static String getStrField(Entity entry, String field) {
        if (null != entry) {
            String str = entry.getStr(field);
            if (null != str && str.length() > 0 && !str.toLowerCase().trim().equals("null")) {
                return str.trim();
            }
        }
        return "";
    }

    @Data
    public static class NameIdListSum {
        private String name = "";
        private List<Integer> idList = new ArrayList<>();
        private int sum = 0;
    }

    @Data
    public static class ArticleInfo {
        private int id = 0;
        private String deptName = "";
        private int year = 2018;
        private List<String> fundList = new ArrayList<>();
        private int misconduct = 0;
        private int isRetraction = 0;
        private int isFirstAuth = 0;
        private int isTongXun = 0;
    }


// end
}
